home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-06-16 | 13.2 KB | 238 lines | [TEXT/CCL2] |
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;
- ;;;; choose-folder-dialog
- ;;;;
- ;;;; Dan S. Camper
- ;;;; June, 1994
- ;;;;
- ;;;; This module is intended to offer a replacement to MCL's #'choose-directory-dialog function.
- ;;;; The interface is a bit better, or at least more like the interface used in other Mac
- ;;;; applications. System 7 is required for this to work. You'll also need the resources found
- ;;;; in the included ResEdit document.
- ;;;;
- ;;;; This code was modeled from Greg Robbins' "StandardGetFolder", written in both Think C and
- ;;;; Pascal and distributed on Apple's ETO CD-ROM as a DTS Snippet.
- ;;;;
- ;;;; Usage:
- ;;;;
- ;;;; #'choose-folder-dialog The function itself; it accepts the following keyed parameters:
- ;;;; button-string A "prefix" to the select button on the dialog; defaults to
- ;;;; "Select" (eg, "Select <FolderName>")
- ;;;; position Position of dialog; defaults to centered on the primary screen
- ;;;; select-key Command key equivalent for selecting the directory; must be
- ;;;; a character. The default is nil.
- ;;;; resource-path If a file pathname is specified then the file will should
- ;;;; contain the custom DLOG and DITL resources needed for this
- ;;;; dialog. The default is nil, which indicates that the current
- ;;;; application should have these resources included. This is a
- ;;;; handy parameter to have during development, if you don't want
- ;;;; to load MCL with resources it doesn't usually use.
- ;;;; resource-id The ID number associated with the DLOG and DTIL resources
- ;;;; used in this custom dialog. The default is 128.
- ;;;;
- ;;;; Function returns:
- ;;;;
- ;;;; If successful, the function returns a Lisp pathname (unlike #'choose-directory-dialog, which
- ;;;; returns a string). If the user clicks the cancel button then the function will execute
- ;;;; 'throw-cancel (which you can trap with 'catch-cancel). If an error occurs while resolving a
- ;;;; supposedly-valid user selection the function returns nil.
- ;;;;
- (in-package :ccl)
-
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (require :resources))
-
- (export '(choose-folder-dialog))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defconstant $kGFSelectItem 10 "Select button")
- (defconstant $kDefaultResourceID 128)
-
- (defvar *get-folder-dialog-select-button* nil)
- (defvar *get-folder-dialog-desktop-name* "Desktop" "Displayed name of desktop folder")
- (defvar *get-folder-default-button-string* "Select" "Prefix for select button")
- (defvar *get-folder-select-char* nil "Command key equivalent for selecting the directory; must be a character")
-
- (defrecord (CustomGetFileDataRec :pointer)
- (SFRPtr :StandardFileReply)
- (oldSelectionFSSpec :FSSpec))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defun choose-folder-dialog (&key (button-string *get-folder-default-button-string*)
- (position #@(-1 -1))
- (select-key *get-folder-select-char*)
- (resource-path nil)
- (resource-id $kDefaultResourceID))
- (let ((*get-folder-dialog-select-button* button-string)
- (*get-folder-select-char* select-key))
- (standard-get-folder position resource-id resource-path)))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defun set-button-name (dialog-ptr button-id button-name quote?)
- (rlet ((button-type :signed-integer)
- (button-rect :rect)
- (button-handle :handle))
- (with-pstrs ((select-label-ptr *get-folder-dialog-select-button*)
- (quotes-and-space-ptr (format nil "~A ~A" #\“ #\”))
- (space-ptr " "))
- (#_GetDItem dialog-ptr button-id button-type button-handle button-rect)
- (let* ((text-width (- (pref button-rect :Rect.right)
- (pref button-rect :Rect.left)
- 8))
- (button-label (if quote?
- (truncate-string (format nil "~A ~A~A~A" *get-folder-dialog-select-button* #\“ button-name #\”) text-width)
- (truncate-string (format nil "~A ~A" *get-folder-dialog-select-button* button-name ) text-width))))
- (with-pstrs ((button-label-ptr button-label))
- (#_SetCTitle (%get-ptr button-handle) button-label-ptr)))
- (#_ValidRect button-rect))))
-
- (defun truncate-string (string width &optional (font '("Chicago" 12 :plain :srcor)))
- (let ((ellipsis "…")
- (new-string string))
- (when (and (stringp string)
- (integerp width)
- (plusp width)
- (> (string-width string font) width)
- (< (string-width ellipsis font) width))
- (let* ((mid (floor (length string) 2))
- (prefix (subseq string 0 mid))
- (suffix (subseq string (1+ mid) (length string)))
- (toggle nil))
- (loop while (and (> (string-width (format nil "~A~A~A" prefix ellipsis suffix) font) width)
- (not (equalp prefix ""))
- (not (equalp suffix "")))
- do (if toggle
- (setf prefix (subseq prefix 0 (1- (length prefix))))
- (setf suffix (subseq suffix 1 (length suffix))))
- do (setf toggle (not toggle)))
- (unless (or (equalp prefix "") (equalp suffix ""))
- (setf new-string (format nil "~A~A~A" prefix ellipsis suffix)))))
- new-string))
-
- (defun same-fsspec (fsspec-rec1 fsspec-rec2)
- (and (= (pref fsspec-rec1 :FSSpec.vRefNum) (pref fsspec-rec2 :FSSpec.vRefNum))
- (= (pref fsspec-rec1 :FSSpec.parID) (pref fsspec-rec2 :FSSpec.parID))
- (equalp (pref fsspec-rec1 :FSSpec.name) (pref fsspec-rec2 :FSSpec.name))))
-
- (defpascal MyModalDialogFilter (:ptr theDlgPtr :ptr myEvtRec :ptr item :ptr myDataPtr
- :word)
- (declare (ignore myDataPtr))
- (let ((return-value #$false))
- (when (and *get-folder-select-char*
- (equal (%get-ostype theDlgPtr #.(field-info :WindowRecord 'RefCon)) #$sfMainDialogRefCon)
- (= (pref myEvtRec :EventRecord.What) #$keyDown)
- (not (zerop (logand (pref myEvtRec :EventRecord.Modifiers) #$cmdKey)))
- (equalp (code-char (logand (pref myEvtRec :EventRecord.Message) #$CharCodeMask)) *get-folder-select-char*))
- (%put-word item $kGFSelectItem)
- (setf return-value #$true)
- (rlet ((button-type :signed-integer)
- (button-rect :rect)
- (final-ticks :signed-long)
- (button-handle :handle))
- (#_GetDItem theDlgPtr $kGFSelectItem button-type button-handle button-rect)
- (#_HiliteControl (%get-ptr button-handle) #$inButton)
- (#_Delay 8 final-ticks)
- (#_HiliteControl (%get-ptr button-handle) 0)
- ))
- return-value))
-
- (defpascal MyCustomFileFilter (:ptr myCInfoPBPtr :ptr myDataPtr
- :word)
- (declare (ignore myDataPtr))
- (if (logbitp 4 (pref myCInfoPBPtr :CInfoPBRec.ioFlAttrib))
- #$false
- #$true))
-
- (defpascal MyDialogHook (:word item :ptr theDialogPtr :ptr customDataPtr
- :word)
- (when (equal (%get-ostype theDialogPtr #.(field-info :WindowRecord 'RefCon)) #$sfMainDialogRefCon)
- (rlet ((desktopVRefNum :integer)
- (desktopDirID :longint)
- (tempFSSpec :FSSpec))
- (if (= item $kGFSelectItem)
- (setf item #$sfItemOpenButton))
- (#_FindFolder (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.vRefNum) #$kDesktopFolderType #$kDontCreateFolder desktopVRefNum desktopDirID)
- (when (or (not (same-fsspec (rref customDataPtr :CustomGetFileDataRec.oldSelectionFSSpec) (rref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile)))
- (= item #$sfHookFirstCall)
- (= item #$sfHookChangeSelection)
- (= item #$sfHookRebuildList))
- (if (not (equalp (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.name) ""))
- (set-button-name theDialogPtr $kGFSelectItem (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.name) t)
- (progn
- (if (and (= (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.vRefNum) (%get-word desktopVRefNum))
- (= (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.ParID) (%get-long desktopDirID)))
- (set-button-name theDialogPtr $kGFSelectItem *get-folder-dialog-desktop-name* nil)
- (progn
- (rlet ((short-str :Str63))
- (%put-string short-str "")
- (#_FSMakeFSSpec (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.vRefNum) (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.ParID) short-str tempFSSpec)
- (set-button-name theDialogPtr $kGFSelectItem (pref tempFSSpec :FSSpec.name) t)))))))
- (setf (pref customDataPtr :CustomGetFileDataRec.oldSelectionFSSpec.vRefNum) (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.vRefNum)
- (pref customDataPtr :CustomGetFileDataRec.oldSelectionFSSpec.parID) (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.parID)
- (pref customDataPtr :CustomGetFileDataRec.oldSelectionFSSpec.name) (pref customDataPtr :CustomGetFileDataRec.SFRPtr.sfFile.name))))
- item)
-
- (defun standard-get-folder (position res-id res-path)
- (rlet ((mySFTypeList :SFTypeList)
- (myData :CustomGetFileDataRec)
- (folderFlag :boolean)
- (wasAliasedFlag :boolean))
- ; Make sure #_CustomGetFile is available
- (when (logbitp #$gestaltStandardFile58 (or (gestalt #$gestaltStandardFileAttr) 0))
- (setf (pref myData :StandardFileReply.sfFile.name) ""
- (pref myData :StandardFileReply.sfFile.vRefNum) 0
- (pref myData :StandardFileReply.sfFile.parID) 0)
- (if (pathnamep res-path)
- (progn
- (with-open-resource-file (ref res-path)
- (#_CustomGetFile MyCustomFileFilter 0 mySFTypeList myData res-id position MyDialogHook MyModalDialogFilter (%null-ptr) (%null-ptr) myData)))
- (#_CustomGetFile MyCustomFileFilter 0 mySFTypeList myData res-id position MyDialogHook MyModalDialogFilter (%null-ptr) (%null-ptr) myData))
- (when (pref myData :StandardFileReply.sfGood)
- (when (equalp (pref myData :StandardFileReply.sfFile.name) "")
- ; Nothing selected, get parent folder
- (rlet ((short-str :Str63))
- (%put-string short-str "")
- (let ((err (#_FSMakeFSSpec (pref myData :StandardFileReply.sfFile.vRefNum) (pref myData :StandardFileReply.sfFile.parID) short-str (rref myData :StandardFileReply.sfFile))))
- (setf (pref myData :StandardFileReply.sfGood) (eql err #$noErr)))))
- (when (not (equalp (pref myData :StandardFileReply.sfFile.name) ""))
- ; If we don't have a name at this point then there's an error
- (when (= (pref myData :StandardFileReply.sfFile.parID) 1)
- (setf (pref myData :StandardFileReply.sfIsVolume) t
- (pref myData :StandardFileReply.sfIsFolder) t))
- (let ((alias-err (#_ResolveAliasFile (rref myData :StandardFileReply.sfFile) t folderFlag wasAliasedFlag)))
- (if (neq alias-err #$noErr)
- (setf (pref myData :StandardFileReply.sfGood) nil)
- (if (= (%get-byte folderFlag) #$true)
- (setf (pref myData :StandardFileReply.sfIsFolder) t))))))
- (if (pref myData :StandardFileReply.sfGood)
- ; Build up a valid pathname.
- (let ((final-path nil)
- (keep-looping t)
- (err #$noErr))
- (rlet ((cpb :CInfoPBRec))
- (with-returned-pstrs ((pname (pref myData :StandardFileReply.sfFile.name)))
- (setf (rref cpb :CInfoPBRec.ioVRefNum) (pref myData :StandardFileReply.sfFile.vRefNum)
- (rref cpb :CinfoPBRec.ioNamePtr) pname
- (rref cpb :CInfoPBRec.ioDrParID) 0
- (rref cpb :CInfoPBRec.ioDrDirID) (pref myData :StandardFileReply.sfFile.parID)
- (rref cpb :CInfoPBRec.ioFDirIndex) 0)
- (loop while keep-looping
- do (setf err (#_PBGetCatInfo cpb))
- do (if (eq err #$noErr)
- (progn
- (push (%get-string (rref cpb :CInfoPBRec.ioNamePtr)) final-path)
- (setf keep-looping (not (equal (rref cpb :CInfoPBRec.ioDrDirID) #$fsRtDirID))
- (rref cpb :CInfoPBRec.ioDrDirID) (rref cpb :CInfoPBRec.ioDrParID)
- (rref cpb :CInfoPBRec.ioFDirIndex) -1))
- (setf keep-looping nil)))))
- (when (eql err #$noErr)
- (push :absolute final-path)
- (make-pathname :directory final-path)))
- (throw-cancel)))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (provide :choose-folder-dialog)